home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Development Platforms / HyperCard Related / XCMDs & XFCNs / Alias XFCN / source / alias.c next >
Encoding:
C/C++ Source or Header  |  1991-03-22  |  19.1 KB  |  803 lines  |  [TEXT/MPS ]

  1. /* -----------------------------------------------------------------
  2.     File:            alias.c
  3.                     XFCN to do 7.0 Alias stuff
  4.     
  5.     Version:        see kVersionStr below
  6.     
  7.     History:        11-06-90 1.0d1 JRP    from volCheck.c
  8.                     11-12-90 1.0d2 JRP    put version stuff in vers.h
  9.                     11-13-90 1.0d3 JRP    add trap and gestalt checking
  10.                     11-16-90 1.0d4 JRP    add findStrResId
  11.                                         revise cmpStrtoResNum params
  12.                                         revise returnMsgNum params
  13.                     03-21-91 1.1d1 JRP    add complete return for GetAliasInfo
  14.                     03-22-91 1.1d2 JRP    return GetAliasInfo as return-delimited
  15.     
  16.     Author:            John R. Powers, III
  17.                     Instructional Products Department
  18.                     Apple Computer, Inc.
  19.                     408-974-9851
  20.                     AppleLink: JohnPowers
  21.     
  22.     Copyright:        see vers.h and "copyright" below.
  23.  
  24.     Computer:        Mac IIfx with System v7.0b4
  25.     
  26.     Compiler:        MPW v3.2
  27.  
  28.     Usage:
  29.     
  30.         
  31.         Requires HyperCard version 1.2 or better.
  32.         
  33.     Files:
  34.     
  35.         alias.c            This source file
  36.         alias.r            Rez source
  37.         vers.h            header with version information
  38.         trap.c            source with trap functions
  39.         trap.pro        prototypes for trap.c
  40.         makefileAlias    make file
  41.         aliasLab        HyperCard stack for testing
  42.         
  43.     Full Build:
  44.     
  45.         see makefileAlias
  46.  
  47.         
  48. ----------------------------------------------------------------- */
  49.  
  50. #include    <HyperXCmd.h>
  51. #include    <Types.h>
  52. #include    <CType.h>
  53. #include    <String.h>        /*    strcpy....    */
  54. #include    <Strings.h>        /*    c2pstr....    */
  55. #include    <Memory.h>
  56. #include    <ToolUtils.h>
  57. #include    <Resources.h>
  58. #include    <Packages.h>
  59. #include    <Files.h>
  60. #include    <Errors.h>
  61.  
  62. #include    <Aliases.h>
  63.  
  64. /*
  65.         Constants
  66. */
  67.  
  68. #define        kCterm            '\0'
  69. #define        kColon            ':'
  70. #define        kReturn            '\n'
  71. #define        kComma            ','
  72. #define        kSpace            ','
  73. #define        kCommaStr        ","
  74. #define        kReturnStr        "\n"
  75. #define        kColonStr        ":"
  76. #define        kSpaceStr        " "
  77. #define        kNoStringResource    "Err 00 Missing STR# for "
  78. #define        kNoErrNum        0    /*    don't want to return an error code    */
  79.  
  80. #include "vers.h"                /*     version and copyright info            */
  81.  
  82. enum                            /*    STR# indices                        */
  83.     {
  84.         ksCmdVersion=1,            /*    first of commands                    */
  85.         ksCmdHelp,
  86.         ksCmdMakeAlias,
  87.         ksCmdResolveAlias,
  88.         ksCmdVerifyAlias,        /*    phantom command for debugging only    */
  89.         ksCmdGetAliasInfo,
  90.         ksHelpDesc,                /*    help description                    */
  91.         ksCallback,                /*    callback string for HyperCard        */
  92.         ksErrCommandWhere,        /*    first of error messages                */
  93.         ksErrCommandHuh,        /*    Err 02    */
  94.         ksErrMissingParam,        /*    Err 03    */
  95.         ksErrNoAliasMgr,        /*    Err 04    */
  96.         ksErrEvalExpr,            /*    Err 05    */
  97.         ksErrFSMakeFSSpec,        /*    Err 06    */
  98.         ksErrNewAlias,            /*    Err 07    */
  99.         ksErrResolveAlias,        /*    Err 08    */
  100.         ksErrResolveAliasFile,    /*    Err 09    */
  101.         ksErrGetAliasInfoZone,    /*    Err 10    */
  102.         ksErrGetAliasInfoServer,/*    Err 11    */
  103.         ksErrGetAliasInfoVolume,/*    Err 12    */
  104.         ksErrGetAliasInfoParent    /*    Err 13    */
  105.     };
  106.  
  107. /*
  108.         Prototypes
  109. */
  110.  
  111. void addColon(Str255);
  112. void addStrItemToList(char *, char *);
  113. void addNumItemToList(char *, long);
  114. short cmpStrtoResNum(Str255, short, short);
  115. void convertDataToHexStr(char *, Handle);
  116. void convertFSSpecToStr(char *, FSSpecPtr);
  117. void convertHexStrToData(char *, Handle *);
  118. void convertStrToFSSpec(char *, FSSpecPtr);
  119. void copyPtoCstr(char *, Str255);
  120. Handle copyStrToHand(char *);
  121. void deleteColon(Str255);
  122. short equalPstr(Str255, Str255);
  123. short findStrResId(Str255);
  124. void getItemFromCList(char *, short, char *);
  125. void getItemFromPList(Str255, short, Str255);
  126. void handleToCstr(char *, Handle);
  127. void handleToPstr(Str255, Handle);
  128. void insertParent(char *, char *, char *);
  129. void returnMsgNum(XCmdPtr, short, short, long);
  130.  
  131. #include    "trap.pro"
  132.  
  133. /*
  134.         Main program and entry point for XFCN.
  135. */
  136.  
  137. pascal void    entryPoint(paramPtr)
  138. /*
  139. */
  140.  
  141.     XCmdPtr     paramPtr;        /*    the HyperCard connection        */
  142. {
  143.     char        resultStr[1024],
  144.                 aliasStr[256],
  145.                 copyright[]=kCopyrightStr;
  146.     Str255        command,
  147.                 resPstr,
  148.                 stackPathPstr;
  149.     OSErr        err;
  150.     Handle        hLongStackName;
  151.     Ptr            pLongStackName;
  152.     FSSpec        fileSpec;
  153.     AliasHandle    hAlias;
  154.     Boolean        bWasChanged,
  155.                 bResolveAliasChains,
  156.                 bTargetIsFolder,
  157.                 bWasAliased;
  158.     short        strResId;
  159.     
  160.     *resultStr = kCterm;
  161.     if(paramPtr->paramCount<0)
  162.         return;                    /*    an event, ignore            */
  163.                                 /*    find resource ID to use        */
  164.                                 /*    for our string resource        */
  165.     strResId = findStrResId(kProgNameStr);
  166.     if(!strResId)
  167.     {
  168.         strcpy(resultStr, kNoStringResource);
  169.         strcat(resultStr, kProgNameStr);
  170.         paramPtr->returnValue = copyStrToHand(resultStr);
  171.         return;
  172.     }
  173.                                 /*    check for command            */
  174.     if(paramPtr->paramCount==0)
  175.     {
  176.         returnMsgNum(paramPtr, ksErrCommandWhere, strResId, kNoErrNum);
  177.         return;
  178.     }
  179.     handleToPstr(command, paramPtr->params[0]);
  180.                                 /*     version command?            */
  181.     if(cmpStrtoResNum(command, ksCmdVersion, strResId))
  182.     {
  183.         strcpy(resultStr, kProgNameStr);
  184.         strcat(resultStr, kSpaceStr);
  185.         strcat(resultStr, kVersionStr);
  186.         strcat(resultStr, kSpaceStr);
  187.         strcat(resultStr, kAuthorStr);
  188.         paramPtr->returnValue = copyStrToHand(resultStr);
  189.         return;
  190.     }
  191.                                 /*     help command?                */
  192.     else if(cmpStrtoResNum(command, ksCmdHelp, strResId))
  193.     {
  194.         returnMsgNum(paramPtr, ksHelpDesc, strResId, kNoErrNum);
  195.         return;
  196.     }
  197.                                 /*    is there an alias manager?    */
  198.     else if(!aliasAvailable())
  199.     {
  200.         returnMsgNum(paramPtr, ksErrNoAliasMgr, strResId, kNoErrNum);
  201.         return;
  202.     }
  203.         /*    The following commands require the Alias Manager    */
  204.     if(cmpStrtoResNum(command, ksCmdMakeAlias, strResId))
  205.     {
  206.         if(paramPtr->paramCount==2)
  207.             handleToPstr(stackPathPstr, paramPtr->params[1]);
  208.         else
  209.         {        /*    path not given, use current stack            */
  210.             GetIndString(resPstr, strResId, ksCallback);
  211.             hLongStackName = EvalExpr(paramPtr, resPstr);
  212.             if(hLongStackName==nil || paramPtr->result==xresFail)
  213.             {
  214.                 returnMsgNum(paramPtr, ksErrEvalExpr, strResId, kNoErrNum);
  215.                 return;
  216.             }
  217.                     /*    extract path name from long stack name    */
  218.                     /*    stack "<path to stack>"                    */
  219.             HLock(hLongStackName);
  220.             pLongStackName = *hLongStackName;
  221.             while(*pLongStackName!='"' && *pLongStackName!=0)
  222.                 pLongStackName++;
  223.             pLongStackName++;
  224.             stackPathPstr[0] = 0;
  225.             while(*pLongStackName!='"' && *pLongStackName!=0)
  226.                 stackPathPstr[++stackPathPstr[0]] = *pLongStackName++;
  227.             DisposHandle(hLongStackName);
  228.         }
  229.         if(err = FSMakeFSSpec(0, 0L, stackPathPstr, &fileSpec))
  230.         {
  231.             addStrItemToList(resultStr, p2cstr(stackPathPstr));
  232.             GetIndString(resPstr, strResId, ksErrFSMakeFSSpec);
  233.             addStrItemToList(resultStr, p2cstr(resPstr));
  234.             addNumItemToList(resultStr, err);
  235.             paramPtr->returnValue = copyStrToHand(resultStr);
  236.             return;
  237.         }
  238.         if(err = NewAlias(nil, &fileSpec, &hAlias))
  239.         {
  240.             returnMsgNum(paramPtr, ksErrNewAlias, strResId, err);
  241.             return;
  242.         }
  243.         convertDataToHexStr(aliasStr, (Handle) hAlias);
  244.         paramPtr->returnValue = copyStrToHand(aliasStr);
  245.         DisposHandle((Handle) hAlias);
  246.         return;
  247.     }
  248.     else if(cmpStrtoResNum(command, ksCmdResolveAlias, strResId))
  249.     {
  250.         if(paramPtr->paramCount<2)
  251.         {
  252.             returnMsgNum(paramPtr, ksErrMissingParam, strResId, err);
  253.             return;
  254.         }
  255.         else
  256.         {
  257.             handleToCstr(aliasStr, paramPtr->params[1]);
  258.             convertHexStrToData(aliasStr, &((Handle) hAlias));
  259.             if(err = ResolveAlias(nil, hAlias, &fileSpec, &bWasChanged))
  260.             {
  261.                 returnMsgNum(paramPtr, ksErrResolveAlias, strResId, err);
  262.                 DisposHandle((Handle) hAlias);
  263.                 return;
  264.             }
  265.             bResolveAliasChains = true;
  266.             if(err = ResolveAliasFile(&fileSpec, bResolveAliasChains,
  267.                                         &bTargetIsFolder, &bWasAliased))
  268.             {
  269.                 returnMsgNum(paramPtr, ksErrResolveAliasFile, strResId, err);
  270.                 DisposHandle((Handle) hAlias);
  271.                 return;
  272.             }
  273.             convertFSSpecToStr(resultStr, &fileSpec);
  274.             paramPtr->returnValue = copyStrToHand(resultStr);
  275.             DisposHandle((Handle) hAlias);
  276.             return;
  277.         }
  278.     }
  279.     else if(cmpStrtoResNum(command, ksCmdVerifyAlias, strResId))
  280.     {
  281.         if(paramPtr->paramCount<2)
  282.         {
  283.             returnMsgNum(paramPtr, ksErrMissingParam, strResId, err);
  284.             return;
  285.         }
  286.         else
  287.         {
  288.             handleToCstr(aliasStr, paramPtr->params[1]);
  289.             convertHexStrToData(aliasStr, &((Handle) hAlias));
  290.             convertDataToHexStr(resultStr, (Handle) hAlias);
  291.             paramPtr->returnValue = copyStrToHand(resultStr);
  292.             DisposHandle((Handle) hAlias);
  293.             return;
  294.         }
  295.     }
  296.     else if(cmpStrtoResNum(command, ksCmdGetAliasInfo, strResId))
  297.     {
  298.         if(paramPtr->paramCount<2)
  299.         {
  300.             returnMsgNum(paramPtr, ksErrMissingParam, strResId, err);
  301.             return;
  302.         }
  303.         else
  304.         {
  305.             Str63    zonePstr,
  306.                     serverPstr,
  307.                     volumePstr,
  308.                     parentPstr;
  309.             short    index=0,
  310.                     notAtRoot=true;
  311.             /*    
  312.                 We return the alias information from left-to-right,
  313.                 zone-to-target, but build it from right to left.
  314.             */
  315.             handleToCstr(aliasStr, paramPtr->params[1]);
  316.             convertHexStrToData(aliasStr, &((Handle) hAlias));
  317.             *resultStr = 0;
  318.             /*
  319.                 Build path from target up to root.  Separate with colons.
  320.             */
  321.             while(notAtRoot)
  322.             {
  323.                 if(err = GetAliasInfo(hAlias, index++, parentPstr))
  324.                 {
  325.                     returnMsgNum(paramPtr, ksErrGetAliasInfoParent, strResId, index-1);
  326.                     DisposHandle((Handle) hAlias);
  327.                     return;
  328.                 }
  329.                 else if(notAtRoot=(parentPstr[0]!=0))
  330.                 {
  331.                     if(index)
  332.                         insertParent(resultStr, p2cstr(parentPstr), kColonStr);
  333.                     else    /*    first (target) entry in path, no separator    */
  334.                         strcpy(resultStr, p2cstr(parentPstr));
  335.                 }
  336.             }
  337.             /*
  338.                 Continue with volume and colon to complete path
  339.             */
  340.             if(err = GetAliasInfo(hAlias, asiVolumeName, volumePstr))
  341.             {
  342.                 returnMsgNum(paramPtr, ksErrGetAliasInfoVolume, strResId, kNoErrNum);
  343.                 DisposHandle((Handle) hAlias);
  344.                 return;
  345.             }
  346.             insertParent(resultStr, p2cstr(volumePstr), kColonStr);
  347.             /*
  348.                 Continue with server and comma
  349.             */
  350.             if(err = GetAliasInfo(hAlias, asiServerName, serverPstr))
  351.             {
  352.                 returnMsgNum(paramPtr, ksErrGetAliasInfoServer, strResId, kNoErrNum);
  353.                 DisposHandle((Handle) hAlias);
  354.                 return;
  355.             }
  356.             insertParent(resultStr, p2cstr(serverPstr), kReturnStr);
  357.             /*
  358.                 Finally zone and comma
  359.             */
  360.             if(err = GetAliasInfo(hAlias, asiZoneName, zonePstr))
  361.             {
  362.                 returnMsgNum(paramPtr, ksErrGetAliasInfoZone, strResId, kNoErrNum);
  363.                 DisposHandle((Handle) hAlias);
  364.                 return;
  365.             }
  366.             insertParent(resultStr, p2cstr(zonePstr), kReturnStr);
  367.             /*
  368.                 All done, return to caller
  369.             */
  370.             paramPtr->returnValue = copyStrToHand(resultStr);
  371.             DisposHandle((Handle) hAlias);
  372.             return;
  373.         }
  374.     }
  375.     else
  376.     {
  377.         returnMsgNum(paramPtr, ksErrCommandHuh, strResId, kNoErrNum);
  378.         return;
  379.     }
  380. }    /*    --------------------------------------------    entryPoint    */
  381.  
  382. /*
  383.         Functions
  384. */
  385.  
  386. void addColon(volName)
  387. /*
  388.     Add a colon to the end of volName.
  389.     If one is already there, leave it.
  390. */
  391.     Str255    volName;
  392. {
  393.     if(volName[volName[0]]!=kColon)
  394.     {    /*    add colon at end    */
  395.         volName[0]++;
  396.         volName[volName[0]] = kColon;
  397.     }
  398. }    /*    --------------------------------------------    addColon    */
  399.  
  400. void addNumItemToList(pList, num)
  401. /*
  402.     Add num to pList.
  403.     
  404.     Preceed each item with a comma.
  405.     The comma will not be added as
  406.     the first item in a string or
  407.     the first item in a line.
  408. */
  409.     char    *pList;
  410.     long    num;
  411. {
  412.     Str255    tempPstr;
  413.  
  414.     NumToString(num, tempPstr);
  415.     addStrItemToList(pList, p2cstr(tempPstr));
  416. }    /*    --------------------------------------------    addNumItemToList    */
  417.  
  418. void addStrItemToList(pList, pAdd)
  419. /*
  420.     Add pAdd to pList.
  421.     
  422.     Preceed each item with a comma.
  423.     The comma will not be added as
  424.     the first item in a string or
  425.     the first item in a line.
  426. */
  427.     char    *pList,
  428.             *pAdd;
  429. {
  430.     short    len;
  431.     
  432.     if(len=strlen(pList))
  433.         if(pList[len-1]!=kReturn)
  434.             strcat(pList, kCommaStr);
  435.     strcat(pList, pAdd);
  436. }    /*    --------------------------------------------    addStrItemToList    */
  437.  
  438. short cmpStrtoResNum(theString, theStrNum, strResId)
  439. /*
  440.  
  441.     08/17/90 1.0a2 MJP    new (GFXMenu)
  442.     11-16-90 1.0d4 JRP    add strResId as parameter
  443. */
  444.     Str255        theString;
  445.     short        theStrNum,
  446.                 strResId;
  447.     
  448. {
  449.     Str255        ResString;
  450.     
  451.     GetIndString(ResString, strResId, theStrNum);
  452.     return (equalPstr(ResString, theString));
  453. }    /*    --------------------------------------------    cmpStrtoResNum    */
  454.  
  455. void convertDataToHexStr(hexStr, han)
  456. /*
  457.     Convert the handle data to a string
  458.     of ASCII hex characters.
  459. */
  460.     char    *hexStr;
  461.     Handle    han;
  462. {
  463.     Ptr        pData;
  464.     long    i;
  465.     Size    hanSize;
  466.     Byte    dataByte,
  467.             byteValue,
  468.             hiNibble,
  469.             loNibble;
  470.     
  471.     hanSize = GetHandleSize(han);
  472.     HLock(han);
  473.     pData = *han;
  474.     for(i=0; i<hanSize; i++)
  475.     {
  476.         dataByte = *pData++;
  477.         byteValue = (dataByte & 0xF0) >> 4;
  478.         if(byteValue>9)
  479.             hiNibble = 'A' + (byteValue - 10);
  480.         else
  481.             hiNibble = '0' + byteValue;
  482.         byteValue = dataByte & 0x0F;
  483.         if(byteValue>9)
  484.             loNibble = 'A' + (byteValue - 10);
  485.         else
  486.             loNibble = '0' + byteValue;
  487.         *hexStr++ = hiNibble;
  488.         *hexStr++ = loNibble;
  489.     }
  490.     HUnlock(han);
  491.     *hexStr = '\0';        /*    add terminator    */
  492. }    /*    --------------------------------------- convertDataToHexStr    */
  493.  
  494. void convertFSSpecToStr(str, pSpec)
  495. /*
  496.     Convert an FSSpec to a string.
  497. */
  498.     char        *str;
  499.     FSSpecPtr    pSpec;
  500. {
  501.     char        tempStr[256];
  502.     
  503.     *str = 0;
  504.     addNumItemToList(str, pSpec->vRefNum);
  505.     addNumItemToList(str, pSpec->parID);
  506.     copyPtoCstr(tempStr, pSpec->name);
  507.     addStrItemToList(str, tempStr);
  508. }    /*    --------------------------------------- convertFSSpecToStr    */
  509.  
  510. void convertHexStrToData(hexStr, hanReturn)
  511. /*
  512.     Convert the ASCII hex characters
  513.     to a handle of data.
  514.     
  515.     Handle is created in this function.
  516. */
  517.     char    *hexStr;
  518.     Handle    *hanReturn;
  519. {
  520.     Handle    han;
  521.     Ptr        pData;
  522.     short    lenHexStr;
  523.     Size    hanSize;
  524.     Byte    hiAsciiNibble,
  525.             loAsciiNibble,
  526.             dataValue;
  527.     
  528.     lenHexStr = strlen(hexStr);
  529.     if(lenHexStr & 0x01)
  530.     {
  531.         DebugStr("\pconvertHexStrToData: hex string is odd length");
  532.         *hanReturn = nil;
  533.         return;
  534.     }
  535.     hanSize = lenHexStr >> 1;            /*    one-half                */
  536.     han = NewHandleClear(hanSize);
  537.     if(han==nil || MemError())
  538.     {
  539.         DebugStr("\pconvertHexStrToData: NewHandleClear failed");
  540.         *hanReturn = nil;
  541.         return;
  542.     }
  543.     HLock(han);
  544.     pData = *han;
  545.     while(*hexStr)
  546.     {
  547.         hiAsciiNibble = *hexStr++;
  548.         if(hiAsciiNibble>'9')
  549.             dataValue = ((10 + hiAsciiNibble - 'A') << 4);
  550.         else
  551.             dataValue = (hiAsciiNibble - '0') << 4;
  552.         loAsciiNibble = *hexStr++;
  553.         if(loAsciiNibble>'9')
  554.             dataValue |= 10 + loAsciiNibble - 'A';
  555.         else
  556.             dataValue |= loAsciiNibble - '0';
  557.         *pData++ = dataValue;
  558.     }
  559.     HUnlock(han);
  560.     *hanReturn = han;
  561. }    /*    --------------------------------------- convertHexStrToData    */
  562.  
  563. void convertStrToFSSpec(listStr, pSpec)
  564. /*
  565.     Convert  a string to an FSSpec.
  566. */
  567.     char        *listStr;
  568.     FSSpecPtr    pSpec;
  569. {
  570.     char        tempStr[256];
  571.     long        tempLong;
  572.     
  573.     getItemFromCList(listStr, 1, tempStr);
  574.     StringToNum(c2pstr(tempStr), &tempLong);
  575.     pSpec->vRefNum = tempLong;
  576.     getItemFromCList(listStr, 2, tempStr);
  577.     StringToNum(c2pstr(tempStr), &(pSpec->parID));
  578.     getItemFromCList(listStr, 3, pSpec->name);
  579. }    /*    --------------------------------------- convertStrToFSSpec    */
  580.  
  581. void copyPtoCstr(Cstr, Pstr)
  582. /*
  583.     Copy the Pstr to the Cstr.
  584.     Don't do it in-place like ToCstr.
  585. */
  586.     Str255    Pstr;
  587.     char    *Cstr;
  588. {
  589.     short    i;
  590.     
  591.     for(i=1; i<=Pstr[0]; i++)
  592.         *Cstr++ = Pstr[i];
  593.     *Cstr = 0;
  594. }    /*    --------------------------------------- copyPtoCstr            */
  595.  
  596. Handle copyStrToHand(str)
  597. /*
  598.     Create a handle and copy the string to it.
  599.     Return the handle.
  600. */
  601.     char *str;
  602. {
  603.     Handle    newHndl;
  604.  
  605.     newHndl = (Handle) NewHandle((long) strlen(str) + 1);
  606.     HLock(newHndl);
  607.     strcpy(*newHndl, str);
  608.     HUnlock(newHndl);
  609.     return(newHndl);
  610. }    /*    --------------------------------------------    copyStrToHand        */
  611.  
  612. void deleteColon(volName)
  613. /*
  614.     Delete a colon from the end of volName.
  615.     If one isn't there, forget it.
  616. */
  617.     Str255    volName;
  618. {
  619.     if(volName[volName[0]]==kColon)
  620.         volName[0]--;
  621. }    /*    --------------------------------------------    deleteColon            */
  622.  
  623. short equalPstr(s1, s2)
  624. /*
  625.     Compare s1 and s2.
  626.     If their contents are equal and they are of equal length, return TRUE.
  627.     Comparison is not case-sensitive.
  628. */
  629.     Str255    s1,
  630.             s2;
  631. {
  632.     short    i,
  633.             c1,
  634.             c2;
  635.     
  636.     if(s1[0]!=s2[0])
  637.         return false;
  638.     for(i=1; i<=s1[0]; i++)
  639.     {
  640.         c1 = tolower(s1[i]);
  641.         c2 = tolower(s2[i]);
  642.         if(c1!=c2)
  643.             return false;
  644.     }
  645.     return true;
  646. }    /*    --------------------------------------------    equalPstr            */
  647.  
  648. short findStrResId(resName)
  649. /*
  650.     Given the name of a STR# resource, return
  651.     its ID number.  This lets us use names instead
  652.     of ID's for tracking STR# resources.
  653. */
  654.     char    *resName;
  655. {
  656.     Handle    hRes;
  657.     short    theID;
  658.     ResType    theType;
  659.     Str255    theName;
  660.     
  661.     strcpy(theName, resName);
  662.     hRes = GetNamedResource('STR#', c2pstr(theName));
  663.     if(!hRes || ResError())
  664.         return 0;
  665.     GetResInfo(hRes, &theID, &theType, theName);
  666.     if(ResError()==resNotFound)
  667.         return 0;
  668.     else
  669.         return theID;
  670. }    /*    --------------------------------------------    findStrResId        */
  671.  
  672. void getItemFromCList(itemListCstr, itemNum, itemCstr)
  673. /*
  674.     Return the itemNum-th item in the itemListCstr.
  675.     Stops at end of string or Return character (end of HyperCard line).
  676.     Returns terminator if item not present.
  677.     Does not return any punctuation (comma or return).
  678. */
  679.     char    *itemListCstr;
  680.     short    itemNum;
  681.     char    *itemCstr;
  682. {
  683.     short    itemCnt=1;
  684.  
  685.                                     /*    Count commas to get itemNum-th item    */
  686.     while(itemCnt<itemNum && *itemListCstr!=0 && *itemListCstr!=kReturn)
  687.         if(*itemListCstr++==kComma)
  688.             itemCnt++;
  689.                                     /*    Copy itemNum-th item                */
  690.                                     /*    skip leading spaces                    */
  691.     if(itemCnt==itemNum)
  692.     {
  693.         while(*itemListCstr!=0 && *itemListCstr!=kReturn && *itemListCstr==kSpace)
  694.             itemListCstr++;
  695.         while(*itemListCstr!=0 && *itemListCstr!=kReturn && *itemListCstr!=kComma)
  696.             *itemCstr++ = *itemListCstr++;
  697.     }
  698.     *itemCstr = kCterm;
  699.         
  700. }    /*    --------------------------------------------    getItemFromCList        */
  701.  
  702. void getItemFromPList(itemListPstr, itemNum, itemPstr)
  703. /*
  704.     Return the itemNum-th item in the itemListPstr
  705. */
  706.     Str255    itemListPstr;
  707.     short    itemNum;
  708.     Str255    itemPstr;
  709. {
  710.     short    i=1,
  711.             j=0,
  712.             itemCnt=1;
  713.                                     /*    Count commas to get itemNum-th item    */
  714.     while(itemCnt<itemNum && i<itemListPstr[0])
  715.         if(itemListPstr[i++]==kComma)
  716.             itemCnt++;
  717.                                     /*    Copy itemNum-th item                */
  718.     if(itemCnt==itemNum)
  719.         while(i<=itemListPstr[0] && itemListPstr[i]!=kComma)
  720.             itemPstr[++j] = itemListPstr[i++];
  721.     itemPstr[0] = j;
  722. }    /*    --------------------------------------------    getItemFromPList        */
  723.  
  724. void handleToCstr(str, hndl)
  725. /*
  726.     Return a copy of the string contained in the handle.
  727.     If the handle is NIL, return an empty string.
  728. */
  729.     char    *str;
  730.     Handle    hndl;
  731. {
  732.     if(hndl==nil)
  733.         *str = kCterm;
  734.     else
  735.     {
  736.         HLock(hndl);
  737.         strcpy(str, *hndl);
  738.         HUnlock(hndl);
  739.     }
  740. }    /*    --------------------------------------------    handleToCstr        */
  741.  
  742. void handleToPstr(str, hndl)
  743. /*
  744.     Return a copy of the string contained in the handle.
  745.     If the handle is NIL, return an empty string.
  746. */
  747.     Str255    str;
  748.     Handle    hndl;
  749. {
  750.     if(hndl==nil)
  751.         str[0] = 0;
  752.     else
  753.     {
  754.         HLock(hndl);
  755.         strcpy((char *)str, *hndl);
  756.         HUnlock(hndl);
  757.         c2pstr(str);
  758.     }
  759. }    /*    --------------------------------------------    handleToPstr        */
  760.  
  761. void insertParent(pList, pParentToAdd, pSeparatorChar)
  762. /*
  763.     Insert pParentToAdd and pSeparatorChar before pList.
  764.  
  765.     Note that pSeparatorChar is a C-string (has terminator.)
  766. */
  767.     char    *pList,
  768.             *pParentToAdd,
  769.             *pSeparatorChar;
  770. {
  771.     char    tempList[1024];
  772.     
  773.     strcpy(tempList, pParentToAdd);
  774.     strcat(tempList, pSeparatorChar);    /*    add suffix following parent    */
  775.     strcat(tempList, pList);            /*    put list after new parent    */
  776.     strcpy(pList, tempList);            /*    copy back to list            */
  777. }    /*    --------------------------------------------    insertParent    */
  778.  
  779. void returnMsgNum(paramPtr, strNum, strResId, num)
  780. /*
  781.     Given the STR# number and a number,
  782.     place the string and the number
  783.     in the return to HyperCard.
  784.     The num is optional, if 0 then it is not placed.
  785.     
  786.     11-16-90 1.0d4 JRP    add strResId as a parameter
  787. */
  788.     XCmdPtr     paramPtr;        /*    the HyperCard connection        */
  789.     short        strNum,
  790.                 strResId;
  791.     long        num;
  792. {
  793.     Str255        resPstr;
  794.     char        resultStr[256];
  795.     
  796.     GetIndString(resPstr, strResId, strNum);
  797.     strcpy(resultStr, p2cstr(resPstr));
  798.     if(num!=kNoErrNum)
  799.         addNumItemToList(resultStr, num);
  800.     paramPtr->returnValue = copyStrToHand(resultStr);
  801. }    /*    --------------------------------------------    returnMsgNum    */
  802.     
  803.